import os
import logging
import openpyxl
from docx import Document
from datetime import datetime, timedelta
from docx.shared import Pt, Cm
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml.shared import qn

# 配置logging
logging.basicConfig(
    level=logging.INFO,
    format='%(lineno)d - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('学风请假.log', encoding='utf-8', mode="w"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)
from docx.oxml import OxmlElement
import re
from pypinyin import lazy_pinyin

# ==================================================
# 配置常量定义
# ==================================================
# 默认行高设置（对应1.02厘米）
DEFAULT_ROW_HEIGHT = 578  # 单位值，约等于1.02厘米

# 文件路径配置
FILE_PATHS = {
    "leave_request_form": "学风建设部请假表.xlsx",
    "output_doc": "自动生成的请假文档.docx"
}

# ==================================================
# Excel数据读取模块
# ==================================================
class ExcelReader:
    @staticmethod
    def read_leave_requests(leave_request_form_path):
        """从学风建设部请假表中读取请假信息"""
        leave_requests = []
        staff_dict = {}
        
        try:
            wb = openpyxl.load_workbook(leave_request_form_path, data_only=True)
            sheet = wb.active
            
            # 查找表头行，确定各列的索引
            header_row = 2
            name_col = student_id_col = college_col = date_col = None
            
            # 查找表头行
            for row in range(1, min(sheet.max_row + 1, 10)):  # 最多检查前10行
                for col in range(1, sheet.max_column + 1):
                    cell_value = sheet.cell(row=row, column=col).value
                    if cell_value and "姓名" in str(cell_value):
                        header_row = row
                        name_col = col
                    elif cell_value and "学号" in str(cell_value):
                        student_id_col = col
                    elif cell_value and ("学院" in str(cell_value) or "部门" in str(cell_value)):
                        college_col = col
                    elif cell_value and ("日期" in str(cell_value) or "请假日期" in str(cell_value)):
                        date_col = col
            
            if not all([name_col, student_id_col, college_col, date_col]):
                logger.error("请假表格格式不符合预期，未找到必要的列")
                logger.debug(f"已找到的列索引 - 姓名: {name_col}, 学号: {student_id_col}, 学院: {college_col}, 日期: {date_col}")
                return []
            
            logger.debug(f"表头行: {header_row}, 列索引 - 姓名: {name_col}, 学号: {student_id_col}, 学院: {college_col}, 日期: {date_col}")
            
            # 从表头行的下一行开始读取数据
            for row in range(header_row + 1, sheet.max_row + 1):
                name = sheet.cell(row=row, column=name_col).value
                student_id = sheet.cell(row=row, column=student_id_col).value
                college = sheet.cell(row=row, column=college_col).value
                leave_date_text = sheet.cell(row=row, column=date_col).value
                
                # 跳过空行
                if not name:
                    continue
                
                logger.debug(f"读取行 {row}: 姓名={name}, 学号={student_id}, 学院={college}, 日期={leave_date_text}")
                
                # 解析请假日期
                leave_dates = []
                if leave_date_text:
                    # 尝试解析日期字符串
                    leave_date_str = str(leave_date_text).strip()
                    logger.debug(f"解析日期文本: '{leave_date_str}'")
                    
                    # 如果是日期对象，转换为字符串
                    if isinstance(leave_date_text, datetime):
                        leave_date_str = leave_date_text.strftime("%m月%d日")
                        leave_dates.append(leave_date_str)
                    else:
                        # 首先按所有可能的分隔符分割（空格、逗号、顿号等）
                        import re
                        all_date_parts = re.split(r'[\s,，;；、]', leave_date_str)
                        
                        # 如果分割后只有一个部分且为空，使用原始字符串
                        if len(all_date_parts) == 1 and not all_date_parts[0].strip():
                            all_date_parts = [leave_date_str]
                        
                        # 处理每个分割后的部分
                        for date_part in all_date_parts:
                            date_part = date_part.strip()
                            if not date_part:
                                continue
                            
                            logger.debug(f"处理日期部分: '{date_part}'")
                            
                            # 检查是否已经是标准格式（如"11月6日"）
                            if '月' in date_part and '日' in date_part:
                                leave_dates.append(date_part)
                                logger.debug(f"已添加标准格式日期: {date_part}")
                            # 处理小数形式（如"11.6"或"11.10"）
                            elif '.' in date_part:
                                try:
                                    # 使用字符串分割更可靠
                                    if '.' in date_part:
                                        parts = date_part.split('.')
                                        if len(parts) == 2:
                                            month_str, day_str = parts
                                            # 确保是数字
                                            if month_str.isdigit() and day_str.isdigit():
                                                month = int(month_str)
                                                day = int(day_str)
                                                # 验证日期有效性
                                                if 1 <= month <= 12 and 1 <= day <= 31:
                                                    formatted_date = f"{month}月{day}日"
                                                    leave_dates.append(formatted_date)
                                                    logger.debug(f"已添加小数格式日期: {formatted_date}")
                                except Exception as e:
                                    logger.debug(f"解析日期部分 '{date_part}' 时出错: {e}")
                
                # 如果没有解析到日期，跳过此行
                if not leave_dates:
                    logger.warning(f"行 {row} 未找到有效日期，跳过")
                    continue
                
                # 使用姓名作为键，合并相同姓名的请假信息
                if name not in staff_dict:
                    staff_dict[name] = {
                        "姓名": name,
                        "学号": student_id,
                        "学院": college,
                        "请假日期": set(leave_dates)  # 使用集合去重
                    }
                else:
                    # 添加新的请假日期
                    staff_dict[name]["请假日期"].update(leave_dates)
            
            # 将字典转换为列表，并将日期集合转换为列表
            for name, info in staff_dict.items():
                leave_requests.append({
                    "姓名": info["姓名"],
                    "学号": info["学号"],
                    "学院": info["学院"],
                    "请假日期": list(info["请假日期"])
                })
            
        except Exception as e:
            logger.error(f"读取请假表时出错: {e}")
        
        logger.debug(f"收集到的请假信息: {leave_requests}")
        return leave_requests

# ==================================================
# Word文档处理模块
# ==================================================
class WordFormatter:
    @staticmethod
    def set_cell_vertical_alignment(cell, alignment="center"):
        """设置单元格的垂直对齐方式"""
        # 获取单元格的XML元素
        tc = cell._tc
        tcPr = tc.get_or_add_tcPr()
        
        # 移除现有的垂直对齐设置（如果有）
        for element in tcPr.iterchildren():
            if element.tag.endswith('vAlign'):
                tcPr.remove(element)
                break
        
        # 创建新的垂直对齐元素
        vAlign = OxmlElement('w:vAlign')
        vAlign.set(qn('w:val'), alignment)
        tcPr.append(vAlign)
    
    @staticmethod
    def set_row_height(row, height):
        """使用新方法设置表格行的高度"""
        try:
            # 直接使用docx库的API设置行高
            row.height = height
            
            # 获取行的XML元素
            tr = row._tr
            trPr = tr.get_or_add_trPr()
            
            # 删除现有的行高设置
            for element in list(trPr):
                if element.tag.endswith('trHeight'):
                    trPr.remove(element)
            
            # 创建新的行高元素
            trHeight = OxmlElement('w:trHeight')
            trHeight.set(qn('w:val'), str(height))
            trHeight.set(qn('w:hRule'), 'exact')  # 精确行高
            trPr.append(trHeight)
            
            return True
        except Exception as e:
            logger.error(f"设置行高时出错: {e}")
            return False
    
    @staticmethod
    def set_cell_format(cell, text):
        """设置单元格的文本、字体和格式"""
        # 清空单元格内容
        for paragraph in cell.paragraphs:
            for run in paragraph.runs:
                run.text = ""
        
        # 如果单元格中没有段落，添加一个
        if not cell.paragraphs:
            cell.add_paragraph()
        
        # 获取第一个段落
        paragraph = cell.paragraphs[0]
        paragraph.text = ""  # 清空段落文本
        
        # 分离中文和非中文（数字和英文）
        # 匹配中文字符
        chinese_pattern = re.compile(r'[\u4e00-\u9fff]+')
        # 匹配非中文字符（数字、英文等）
        non_chinese_pattern = re.compile(r'[^\u4e00-\u9fff]+')
        
        # 查找所有中文和非中文部分
        text_parts = []
        last_end = 0
        
        # 先找出所有中文部分
        for match in chinese_pattern.finditer(text):
            # 如果中文前有非中文，先添加非中文部分
            if match.start() > last_end:
                text_parts.append(('non_chinese', text[last_end:match.start()]))
            # 添加中文部分
            text_parts.append(('chinese', match.group()))
            last_end = match.end()
        
        # 如果最后还有非中文部分，添加它
        if last_end < len(text):
            text_parts.append(('non_chinese', text[last_end:]))
        
        # 如果没有匹配到任何内容，添加整个文本
        if not text_parts:
            text_parts.append(('non_chinese' if non_chinese_pattern.match(text) else 'chinese', text))
        
        # 为每个部分添加不同字体的run
        for part_type, part_text in text_parts:
            run = paragraph.add_run(part_text)
            
            # 设置字体大小为五号（约10.5磅）
            run.font.size = Pt(10.5)
            
            if part_type == 'chinese':
                # 中文使用仿宋_GB2312
                run.font.name = '仿宋_GB2312'
                run._element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋_GB2312')
            else:
                # 数字和英文使用Times New Roman
                run.font.name = 'Times New Roman'
                run._element.rPr.rFonts.set(qn('w:ascii'), 'Times New Roman')
                run._element.rPr.rFonts.set(qn('w:hAnsi'), 'Times New Roman')
        
        # 设置段落水平居中对齐
        paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        
        # 设置单元格垂直居中对齐
        WordFormatter.set_cell_vertical_alignment(cell, "center")

# ==================================================
# 请假文档处理模块
# ==================================================
class LeaveDocumentProcessor:
    @staticmethod
    def add_leave_info_to_document(output_doc_path, leave_requests):
        """将请假信息按学院顺序添加到现有文档的表格中"""
        try:
            # 打开现有文档
            doc = Document(output_doc_path)
            
            # 查找表格
            if len(doc.tables) == 0:
                logger.error("文档中未找到表格")
                return False
            
            table = doc.tables[0]  # 假设第一个表格是需要填写的表格
            
            # 设置行高
            row_height = 578  # 约1.02厘米
            
            # 获取表格中现有的数据行
            existing_entries = []
            existing_names = {}
            
            # 跳过表头行（第0行），读取现有数据
            for i in range(1, len(table.rows)):
                row = table.rows[i]
                if len(row.cells) >= 4:
                    name = row.cells[1].text.strip()
                    if name:
                        existing_names[name] = i  # 记录姓名对应的行索引
                        existing_entries.append({
                            "姓名": name,
                            "学号": row.cells[0].text.strip(),
                            "学院": row.cells[2].text.strip(),
                            "请假日期": row.cells[3].text.strip().split('、')
                        })
            
            logger.info(f"现有表格中有 {len(existing_entries)} 名人员的请假信息")
            
            # 合并现有数据和新数据
            all_entries = {}
            updated_entries = []
            new_entries = []
            
            # 先添加所有现有人员
            for entry in existing_entries:
                all_entries[entry["姓名"]] = entry.copy()
            
            # 处理新的请假信息
            for request in leave_requests:
                name = request["姓名"]
                
                if name in all_entries:
                    # 找到现有人员，合并请假日期
                    all_entries[name]["请假日期"] = list(set(all_entries[name]["请假日期"] + request["请假日期"]))
                    updated_entries.append(name)
                else:
                    # 新人员
                    all_entries[name] = request.copy()
                    new_entries.append(request)
            
            # 转换为列表并按学院拼音和学号排序
            sorted_entries = sorted(all_entries.values(), key=lambda x: (
                lazy_pinyin(x["学院"] if x["学院"] else ""),  # 先按学院拼音排序
                x["学号"] if x["学号"] else ""   # 相同学院按学号排序
            ))
            
            # 清空表格中的数据行（保留表头）
            # 从下往上删除以避免索引问题
            for i in range(len(table.rows) - 1, 0, -1):
                row = table.rows[i]
                table._tbl.remove(row._tr)
            
            # 重新构建表格内容
            for entry in sorted_entries:
                new_row = table.add_row()
                
                # 填写各列数据
                combined_dates = "、".join(entry["请假日期"])
                
                # 确保行有足够的单元格
                if len(new_row.cells) >= 4:
                    WordFormatter.set_cell_format(new_row.cells[0], str(entry["学号"]) if entry["学号"] else "")
                    WordFormatter.set_cell_format(new_row.cells[1], entry["姓名"])
                    WordFormatter.set_cell_format(new_row.cells[2], entry["学院"] if entry["学院"] else "")
                    WordFormatter.set_cell_format(new_row.cells[3], combined_dates)
                
                # 设置行高
                WordFormatter.set_row_height(new_row, row_height)
            
            # 为所有行设置行高（确保一致性）
            for row in table.rows:
                WordFormatter.set_row_height(row, row_height)
            
            # 保存文档
            doc.save(output_doc_path)
            logger.info(f"请假信息已添加到文档: {output_doc_path}")
            logger.info(f"更新了 {len(updated_entries)} 名现有人员的请假信息")
            logger.info(f"添加了 {len(new_entries)} 名新人员的请假信息")
            return True
        
        except Exception as e:
            logger.error(f"添加请假信息到文档时出错: {e}")
            return False

# ==================================================
# 主程序入口
# ==================================================
def main():
    logger.info("开始处理学风请假信息")
    
    # 1. 读取学风建设部请假表中的数据
    logger.info(f"正在读取请假表: {FILE_PATHS['leave_request_form']}")
    staff_info_list = ExcelReader.read_leave_requests(FILE_PATHS["leave_request_form"])
    
    if not staff_info_list:
        logger.warning("未找到有效的请假信息，程序终止")
        return
    
    logger.info(f"成功读取 {len(staff_info_list)} 条请假信息")
    
    # 2. 检查输出文档是否存在
    if not os.path.exists(FILE_PATHS["output_doc"]):
        logger.error(f"输出文档不存在: {FILE_PATHS['output_doc']}")
        logger.error("请先运行自动请假.py生成基础文档")
        return
    
    # 3. 添加请假信息到文档
    success = LeaveDocumentProcessor.add_leave_info_to_document(FILE_PATHS["output_doc"], staff_info_list)
    
    if success:
        logger.info("请假信息添加成功！")
    else:
        logger.error("添加请假信息失败，请检查错误信息。")

if __name__ == "__main__":
    main()